using Consul;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using System.Net;

namespace AuthService.Infrastructure.Services;

/// <summary>
/// Consul服务注册接口
/// </summary>
public interface IConsulServiceRegistry
{
    /// <summary>
    /// 注册服务到Consul
    /// </summary>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>是否成功</returns>
    Task<bool> RegisterServiceAsync(CancellationToken cancellationToken = default);

    /// <summary>
    /// 从Consul注销服务
    /// </summary>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>是否成功</returns>
    Task<bool> DeregisterServiceAsync(CancellationToken cancellationToken = default);

    /// <summary>
    /// 获取服务实例
    /// </summary>
    /// <param name="serviceName">服务名称</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>服务实例列表</returns>
    Task<IEnumerable<ServiceInstance>> GetServiceInstancesAsync(string serviceName, CancellationToken cancellationToken = default);

    /// <summary>
    /// 获取健康的服务实例
    /// </summary>
    /// <param name="serviceName">服务名称</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>健康的服务实例列表</returns>
    Task<IEnumerable<ServiceInstance>> GetHealthyServiceInstancesAsync(string serviceName, CancellationToken cancellationToken = default);
}

/// <summary>
/// 服务实例信息
/// </summary>
public class ServiceInstance
{
    /// <summary>
    /// 服务ID
    /// </summary>
    public string Id { get; set; } = string.Empty;

    /// <summary>
    /// 服务名称
    /// </summary>
    public string Name { get; set; } = string.Empty;

    /// <summary>
    /// 服务地址
    /// </summary>
    public string Address { get; set; } = string.Empty;

    /// <summary>
    /// 服务端口
    /// </summary>
    public int Port { get; set; }

    /// <summary>
    /// 服务标签
    /// </summary>
    public List<string> Tags { get; set; } = new();

    /// <summary>
    /// 服务元数据
    /// </summary>
    public Dictionary<string, string> Meta { get; set; } = new();

    /// <summary>
    /// 是否健康
    /// </summary>
    public bool IsHealthy { get; set; }

    /// <summary>
    /// 服务URL
    /// </summary>
    public string Url => $"http://{Address}:{Port}";
}

/// <summary>
/// Consul服务注册实现
/// </summary>
public class ConsulServiceRegistry : IConsulServiceRegistry
{
    private readonly IConsulClient _consulClient;
    private readonly IConfiguration _configuration;
    private readonly ILogger<ConsulServiceRegistry> _logger;
    private readonly ConsulConfig _consulConfig;
    private string? _serviceId;

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="consulClient">Consul客户端</param>
    /// <param name="configuration">配置</param>
    /// <param name="logger">日志记录器</param>
    public ConsulServiceRegistry(IConsulClient consulClient, IConfiguration configuration, ILogger<ConsulServiceRegistry> logger)
    {
        _consulClient = consulClient;
        _configuration = configuration;
        _logger = logger;
        _consulConfig = _configuration.GetSection("Consul").Get<ConsulConfig>() ?? new ConsulConfig();
    }

    /// <summary>
    /// 注册服务到Consul
    /// </summary>
    public async Task<bool> RegisterServiceAsync(CancellationToken cancellationToken = default)
    {
        try
        {
            // 生成服务ID
            _serviceId = $"{_consulConfig.ServiceName}-{Environment.MachineName}-{_consulConfig.ServicePort}";

            // 创建服务注册信息
            var registration = new AgentServiceRegistration
            {
                ID = _serviceId,
                Name = _consulConfig.ServiceName,
                Address = _consulConfig.ServiceAddress,
                Port = _consulConfig.ServicePort,
                Tags = _consulConfig.ServiceTags.ToArray(),
                Meta = _consulConfig.ServiceMeta,
                Check = CreateHealthCheck()
            };

            // 注册服务
            var result = await _consulClient.Agent.ServiceRegister(registration);

            if (result.StatusCode == HttpStatusCode.OK)
            {
                _logger.LogInformation("服务注册成功: {ServiceId}, 地址: {Address}:{Port}",
                    _serviceId, _consulConfig.ServiceAddress, _consulConfig.ServicePort);
                return true;
            }
            else
            {
                _logger.LogError("服务注册失败: {StatusCode}", result.StatusCode);
                return false;
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "服务注册异常");
            return false;
        }
    }

    /// <summary>
    /// 从Consul注销服务
    /// </summary>
    public async Task<bool> DeregisterServiceAsync(CancellationToken cancellationToken = default)
    {
        try
        {
            if (string.IsNullOrEmpty(_serviceId))
            {
                _logger.LogWarning("服务ID为空，无法注销服务");
                return false;
            }

            var result = await _consulClient.Agent.ServiceDeregister(_serviceId);

            if (result.StatusCode == HttpStatusCode.OK)
            {
                _logger.LogInformation("服务注销成功: {ServiceId}", _serviceId);
                return true;
            }
            else
            {
                _logger.LogError("服务注销失败: {StatusCode}", result.StatusCode);
                return false;
            }
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "服务注销异常");
            return false;
        }
    }

    /// <summary>
    /// 获取服务实例
    /// </summary>
    public async Task<IEnumerable<ServiceInstance>> GetServiceInstancesAsync(string serviceName, CancellationToken cancellationToken = default)
    {
        try
        {
            var result = await _consulClient.Catalog.Service(serviceName);

            if (result.StatusCode != HttpStatusCode.OK)
            {
                _logger.LogWarning("获取服务实例失败: {ServiceName}, StatusCode: {StatusCode}", serviceName, result.StatusCode);
                return Enumerable.Empty<ServiceInstance>();
            }

            return result.Response.Select(service => new ServiceInstance
            {
                Id = service.ServiceID,
                Name = service.ServiceName,
                Address = service.ServiceAddress,
                Port = service.ServicePort,
                Tags = service.ServiceTags?.ToList() ?? new List<string>(),
                Meta = service.ServiceMeta?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value) ?? new Dictionary<string, string>(),
                IsHealthy = true // 这里需要额外的健康检查查询
            });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "获取服务实例异常: {ServiceName}", serviceName);
            return Enumerable.Empty<ServiceInstance>();
        }
    }

    /// <summary>
    /// 获取健康的服务实例
    /// </summary>
    public async Task<IEnumerable<ServiceInstance>> GetHealthyServiceInstancesAsync(string serviceName, CancellationToken cancellationToken = default)
    {
        try
        {
            var result = await _consulClient.Health.Service(serviceName, string.Empty, true); // 只获取健康的实例

            if (result.StatusCode != HttpStatusCode.OK)
            {
                _logger.LogWarning("获取健康服务实例失败: {ServiceName}, StatusCode: {StatusCode}", serviceName, result.StatusCode);
                return Enumerable.Empty<ServiceInstance>();
            }

            return result.Response.Select(entry => new ServiceInstance
            {
                Id = entry.Service.ID,
                Name = entry.Service.Service,
                Address = entry.Service.Address,
                Port = entry.Service.Port,
                Tags = entry.Service.Tags?.ToList() ?? new List<string>(),
                Meta = entry.Service.Meta?.ToDictionary(kvp => kvp.Key, kvp => kvp.Value) ?? new Dictionary<string, string>(),
                IsHealthy = entry.Checks.All(check => check.Status == HealthStatus.Passing)
            });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "获取健康服务实例异常: {ServiceName}", serviceName);
            return Enumerable.Empty<ServiceInstance>();
        }
    }

    /// <summary>
    /// 创建健康检查配置
    /// </summary>
    /// <returns>健康检查配置</returns>
    private AgentServiceCheck CreateHealthCheck()
    {
        // 如果健康检查被禁用，返回 null
        if (string.IsNullOrEmpty(_consulConfig.HealthCheckPath) || _consulConfig.HealthCheckInterval <= 0)
        {
            _logger.LogInformation("健康检查已禁用（开发环境）");
            return null;
        }

        return new AgentServiceCheck
        {
            HTTP = $"http://{_consulConfig.ServiceAddress}:{_consulConfig.ServicePort}{_consulConfig.HealthCheckPath}",
            Interval = TimeSpan.FromSeconds(_consulConfig.HealthCheckInterval),
            Timeout = TimeSpan.FromSeconds(_consulConfig.HealthCheckTimeout),
            DeregisterCriticalServiceAfter = TimeSpan.FromMinutes(_consulConfig.DeregisterCriticalServiceAfter)
        };
    }
}

/// <summary>
/// Consul配置
/// </summary>
public class ConsulConfig
{
    /// <summary>
    /// Consul服务器地址
    /// </summary>
    public string ConsulAddress { get; set; } = "http://localhost:8500";

    /// <summary>
    /// 数据中心名称
    /// </summary>
    public string Datacenter { get; set; } = "dc1";

    /// <summary>
    /// 服务名称
    /// </summary>
    public string ServiceName { get; set; } = "auth-service";

    /// <summary>
    /// 服务地址
    /// </summary>
    public string ServiceAddress { get; set; } = "localhost";

    /// <summary>
    /// 服务端口
    /// </summary>
    public int ServicePort { get; set; } = 5000;

    /// <summary>
    /// 服务标签
    /// </summary>
    public List<string> ServiceTags { get; set; } = new() { "auth", "api", "microservice" };

    /// <summary>
    /// 服务元数据
    /// </summary>
    public Dictionary<string, string> ServiceMeta { get; set; } = new();

    /// <summary>
    /// 健康检查路径
    /// </summary>
    public string HealthCheckPath { get; set; } = "/health";

    /// <summary>
    /// 健康检查间隔（秒）
    /// </summary>
    public int HealthCheckInterval { get; set; } = 10;

    /// <summary>
    /// 健康检查超时（秒）
    /// </summary>
    public int HealthCheckTimeout { get; set; } = 5;

    /// <summary>
    /// 关键服务注销时间（分钟）
    /// </summary>
    public int DeregisterCriticalServiceAfter { get; set; } = 30;
}
